#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
#endregion

namespace XnaGameComponents
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class CockpitCamera : DrawableGameComponent,Camera
    {
        public CockpitCamera(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }

        private bool cameraStateKeyDown;
        private int cameraState;
        private float avatarXaw;
        private float avatarYaw;
        private float avatarZaw;
        // Set rates in world units per 1/60th second (the default fixed-step interval).
        private float rotationSpeed = 1f / 60f;
        //float forwardSpeed = 500f / 60f;
        private float forwardSpeed = 500f / 60f;
        private int fastFactor=5;
        protected Vector3 avatarPosition = new Vector3(0, 2, -20);
        Vector3 cameraReference = new Vector3(0, 0, 10);
        static float viewAngle = MathHelper.PiOver4;
        static float nearClip = 1.0f;
        static float farClip = 4096.0f;
        private bool engineStarted;
        private bool doEngineAction;
        private float engineStoppedPosition;
        private float engineStartedPosition;
        MouseState mousePos;
        private Model model;
        private string modelPath;
        private Vector3[] modelOffset=new Vector3[3];

        public Vector3[] ModelOffset
        {
            get { return modelOffset; }
            set { modelOffset = value; }
        }


        public string Model
        {
            get { return modelPath; }
            set { modelPath = value; }
        }



        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        protected override void LoadContent()
        {
            base.LoadContent();
            model = new ContentManager(Game.Services).Load<Model>(modelPath);
            engineStartedPosition = Position.Y + 300;
            engineStoppedPosition = Position.Y;
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            GetCurrentCamera();
            UpdateAvatarPosition();
            base.Update(gameTime);
            IGameConfiguration config = (IGameConfiguration)Game.Services.GetService(typeof(IGameConfiguration));
            if (Keyboard.GetState().IsKeyDown(config["Engine"]))
                doEngineAction = true;
            if (!engineStarted && doEngineAction)
                StartEngine();
            else if (doEngineAction)
                StopEngine();
        }

        private void StopEngine()
        {
            float newy = (float)Math.Log(Position.Y - engineStoppedPosition);
            avatarPosition.Y -= newy;
            if (newy == 0)
            {
                doEngineAction = false;
                engineStarted = false;
            }
        }

        private void StartEngine()
        {

            float newy = (float)Math.Log(engineStartedPosition - avatarPosition.Y);
            avatarPosition.Y += newy;
            if (newy == 0)
            {
                doEngineAction = false;
                engineStarted = true;
            }
        }

        private void GetCurrentCamera()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            GamePadState currentState = GamePad.GetState(PlayerIndex.One);

            // Toggle the state of the camera.
            if (keyboardState.IsKeyDown(Keys.Tab) || (currentState.Buttons.LeftShoulder == ButtonState.Pressed))
            {
                cameraStateKeyDown = true;
            }
            else if (cameraStateKeyDown == true)
            {
                cameraStateKeyDown = false;
                cameraState += 1;
                cameraState %= 3;
            }
        }

        private void UpdateAvatarPosition()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            GamePadState currentState = GamePad.GetState(PlayerIndex.One);
            if (mousePos == null)
            {
                Mouse.SetPosition(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
                mousePos = Mouse.GetState();
            }
            MouseState mouseState = Mouse.GetState();

            avatarYaw += (mousePos.X - mouseState.X)*rotationSpeed/10;
            avatarXaw -= (mousePos.Y - mouseState.Y)*rotationSpeed/10;
            avatarZaw = avatarYaw;
            Mouse.SetPosition(mousePos.X, mousePos.Y);
            IGameConfiguration config = (IGameConfiguration)Game.Services.GetService(typeof(IGameConfiguration));
            if (engineStarted)
            {
                if (keyboardState.IsKeyDown(config["Left"]) || (currentState.DPad.Left == ButtonState.Pressed))
                {
                    Vector3 v;
                    if (keyboardState.IsKeyDown(Keys.LeftShift))
                        v = new Vector3(forwardSpeed * fastFactor, 0, 0);
                    else
                        v = new Vector3(forwardSpeed, 0, 0);
                    Matrix sideMovement = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarYaw);
                    v = Vector3.Transform(v, sideMovement);
                    avatarPosition.Z += v.Z;
                    avatarPosition.X += v.X;
                }
                if (keyboardState.IsKeyDown(config["Right"]) || (currentState.DPad.Right == ButtonState.Pressed))
                {
                    Vector3 v;
                    if (keyboardState.IsKeyDown(Keys.LeftShift))
                        v = new Vector3(-forwardSpeed * fastFactor, 0, 0);
                    else
                        v = new Vector3(-forwardSpeed, 0, 0);
                    Matrix sideMovement = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarYaw);
                    v = Vector3.Transform(v, sideMovement);
                    avatarPosition.Z += v.Z;
                    avatarPosition.X += v.X;
                }
                if (keyboardState.IsKeyDown(config["Up"]) || (currentState.DPad.Up == ButtonState.Pressed))
                {
                    Vector3 v;
                    if (keyboardState.IsKeyDown(Keys.LeftShift))
                        v = new Vector3(0, 0, forwardSpeed * fastFactor);
                    else
                        v = new Vector3(0, 0, forwardSpeed);
                    Matrix forwardMovement = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarZaw);
                    v = Vector3.Transform(v, forwardMovement);
                    avatarPosition += v;
                }
                if (keyboardState.IsKeyDown(config["Down"]) || (currentState.DPad.Down == ButtonState.Pressed))
                {
                    Vector3 v;
                    if (keyboardState.IsKeyDown(Keys.LeftShift))
                        v = new Vector3(avatarZaw * fastFactor, 0, -forwardSpeed * fastFactor);
                    else
                        v = new Vector3(avatarZaw, 0, -forwardSpeed);
                    Matrix forwardMovement = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarZaw);
                    v = Vector3.Transform(v, forwardMovement);
                    avatarPosition += v;
                }
            }
        }

        public override void Draw(GameTime gameTime)
        {
            UpdateCamera();
            World = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarYaw) * Matrix.CreateTranslation(avatarPosition);
            base.Draw(gameTime);
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.View = View*Matrix.CreateRotationY(MathHelper.Pi)*Matrix.CreateRotationZ(-avatarZaw)*Matrix.CreateTranslation(modelOffset[cameraState]);
                    effect.Projection = Projection;
                    effect.World = transforms[mesh.ParentBone.Index] * (World / 2);
                }
                mesh.Draw();
            }
        }

        void UpdateCamera()
        {
            // Calculate the camera's current position.
            Vector3 cameraPosition = avatarPosition;


            // Calculate the position the camera is looking at.
            Vector3 cameraLookat = cameraPosition + Target;

            // Set up the view matrix and projection matrix.
            View = Matrix.CreateLookAt(cameraPosition, cameraLookat, Vector3.Transform(new Vector3(0.0f, 1.0f, 0.0f),Matrix.CreateRotationZ(-avatarZaw)));

            Viewport viewport = GraphicsDevice.Viewport;
            float aspectRatio = (float)viewport.Width / (float)viewport.Height;

            Projection = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
        }

        #region Camera Membres

        private Matrix world;

        public Matrix World
        {
            get { return world; }
            set { world = value; }
        }

        private Matrix view;

        public Matrix View
        {
            get { return view; }
            set { view = value; }
        }

        private Matrix proj;

        public Matrix Projection
        {
            get { return proj; }
            set { proj = value; }
        }

        public Vector3 Target
        {
            get
            {
                Matrix rotationMatrix = Matrix.CreateRotationX(avatarXaw) * Matrix.CreateRotationY(avatarYaw);

                // Create a vector pointing the direction the camera is facing.
                return Vector3.Transform(cameraReference, rotationMatrix);
            }
        }

        public Vector3 Position
        {
            get { return avatarPosition; }
            set { avatarPosition = value; }
        }

        public void Move(Vector3 v, bool relative)
        {
            if (relative)
                MoveRelative(v);
            else
                MoveAbsolute(v);
        }

        public void MoveRelative(Vector3 v)
        {
            avatarPosition += v;
        }

        public void MoveAbsolute(Vector3 v)
        {
            avatarPosition = v;
        }

        #endregion

        #region IPositionnable Membres

        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private XnaDesignerGenerator.XnaGameComponent site;

        public XnaDesignerGenerator.XnaGameComponent TargetObject
        {
            get { return site; }
            set { site = value; }
        }

        #endregion
    }
}


